package ch.ethz.fcl.metrobuzz.render.interchange.temporal;

import java.util.ArrayList;
import java.util.List;

import org.lwjgl.opengl.GL11;

import ch.ethz.fcl.metrobuzz.MBContainer;
import ch.ethz.fcl.metrobuzz.MBProperty;
import ch.ethz.fcl.metrobuzz.controller.external.ActionEvent;
import ch.ethz.fcl.metrobuzz.data.MBData;
import ch.ethz.fcl.metrobuzz.data.interchange.Interchange;
import ch.ethz.fcl.metrobuzz.data.interchange.Junction;
import ch.ethz.fcl.metrobuzz.data.singapore.SGInterchange;
import ch.ethz.fcl.metrobuzz.render.techniques.circos.CircosMatrix;
import ch.ethz.fcl.metrobuzz.spatialindex.object2d.Point2D;
import ch.ethz.fcl.metrobuzz.spatialindex.object2d.Rectangle;
import ch.ethz.fcl.metrobuzz.spatialindex.object3d.Point3D;
import ch.ethz.fcl.metrobuzz.tools.MBGL;
import ch.ethz.fcl.metrobuzz.tools.MBTools;
import ch.ethz.fcl.metrobuzz.viewer.MBViewer;

public class TemporalRender {
	private SGInterchange sr;
	private MBContainer container;

	private float preDragNum = -1;
	private float curDragNum = 0;

	private float boundSize;
	private float radius;

	private float cameraDist = 0;

	private Interchange selected;
	private boolean insideBound = false;

	private int selectedIndex;
	private List<TemporalElementList> elementLists;

	public static float RADIUS = 60;

	public TemporalRender(MBContainer container, SGInterchange sr) {
		this.container = container;

		this.sr = sr;
		elementLists = new ArrayList<TemporalElementList>();
	}

	public void render(Rectangle bound) {
		MBGL.enableBlend();
		GL11.glDisable(GL11.GL_MULT);

		boolean update = false;
		if (cameraDist != container.getViewer().getCamera().getDistance()
				|| preDragNum != curDragNum || MBData.TIME_CHANGED) {
			cameraDist = container.getViewer().getCamera().getDistance();
			preDragNum = curDragNum;
			update = true;
			MBData.TIME_CHANGED = false;
		}

		if (selected != sr.getSelectedInterchange()) {
			update = true;
			preDragNum = curDragNum = 0;
		}

		selected = sr.getSelectedInterchange();

		radius = container.getViewer().getProjection().getWorldDistance(RADIUS);
		if (MBProperty.SHOW_STATISTICS)
			boundSize = 2 * radius * 1.8f;
		else
			boundSize = 2 * radius * 1.3f;

		if (update) {
			update(elementLists);
		}

		setCircosGlobal();

		for (TemporalElementList ele : elementLists)
			if (ele != null)
				ele.render();

		GL11.glDisable(GL11.GL_BLEND);
	}

	public void handleActionEvent() {
		ActionEvent action = container.getControl().getTuioListener()
				.getTuioEvent();
		if (action == null)
			action = container.getControl().getMouseListener().getLeftMouse();

		if (action != null) {
			Point3D event = container
					.getViewer()
					.getProjection()
					.getWorldCoord(
							new Point2D(action.getEventX(), action.getEventY()),
							0);

			if (action.getAction() == ActionEvent.ACTION_DOWN) {
				for (int i = 0; i < elementLists.size(); i++) {
					TemporalElementList ele = elementLists.get(i);
					if (ele.inIconBound(event.getX(), event.getY())) {
						elementLists.remove(ele);
						selectedIndex = -1;
						selected = null;
						return;
					}
				}

				insideBound = false;
				float minDist = Float.MAX_VALUE;
				for (int i = 0; i < elementLists.size(); i++) {
					TemporalElementList ele = elementLists.get(i);
					float dist = MBTools.distance(event.getX(), event.getY(),
							ele.getInterchange().getCircosCentroid().getX(),
							ele.getInterchange().getCircosCentroid().getY());
					if (ele.inBound(event.getX(), event.getY())
							&& dist < minDist) {
						selectedIndex = i;
						insideBound = true;
						dist = minDist;
					}
				}

				ActionEvent.DRAG_UPDATE = !insideBound;
				MBViewer.newUpdate = true;
			} else if (action.getAction() == ActionEvent.ACTION_DRAG) {
				if (insideBound && selectedIndex != -1) {
					if (event.getX() > elementLists.get(selectedIndex)
							.getInterchange().getCircosCentroid().getX())
						curDragNum = (event.getX()
								- elementLists.get(selectedIndex)
										.getInterchange().getCircosCentroid()
										.getX() + boundSize / 2)
								/ boundSize;
					else
						curDragNum = (event.getX()
								- elementLists.get(selectedIndex)
										.getInterchange().getCircosCentroid()
										.getX() - boundSize / 2)
								/ boundSize;

					if (curDragNum >= 0) {
						elementLists.get(selectedIndex).setRightShift(
								(int) curDragNum);
					} else {
						elementLists.get(selectedIndex).setLeftShift(
								(int) curDragNum);
					}

				} else
					ActionEvent.DRAG_UPDATE = true;
				MBViewer.newUpdate = true;
			} else if (action.getAction() == ActionEvent.ACTION_UP) {
				selectedIndex = -1;
				insideBound = false;
				selected = null;
			}

			if ((action.getAction() == ActionEvent.ACTION_LONGPRESS || action
					.getAction() == ActionEvent.ACTION_DOWN) && !insideBound) {
				sr.selectInterchange(event.getX(), event.getY());

				boolean inList = false;
				for (TemporalElementList tel : elementLists)
					if (tel.getInterchange() == sr.getSelectedInterchange()) {
						inList = true;
						break;
					}

				Interchange interchange = sr.getSelectedInterchange();
				if (!inList && interchange != null) {
					TemporalElementList tel = null;

					CircosMatrix cd = interchange.getData(MBData.startTime,
							MBData.startTime + MBData.period);
					if (interchange instanceof Junction) {
						String id = ((Junction) interchange).getNodes().get(0)
								.getId();
						String name = container.getData().getSGInfo()
								.getMRTStations().getName(id);

						tel = new TemporalElementList(name, container,
								sr.getSelectedInterchange());
						tel.addTemporalElement(new TemporalElement(name, cd,
								interchange.getCircosCentroid(), radius));
					} else {
						tel = new TemporalElementList(container,
								sr.getSelectedInterchange());
						tel.addTemporalElement(new TemporalElement(cd,
								interchange.getCircosCentroid(), radius));
					}

					elementLists.add(tel);
					insideBound = true;
					selectedIndex = elementLists.size() - 1;
				}

				selected = null;
				ActionEvent.DRAG_UPDATE = false;
				MBViewer.newUpdate = true;
			}
		}
	}

	private void update(List<TemporalElementList> lists) {
		for (int n = 0; n < lists.size(); n++) {
			TemporalElementList elements = lists.get(n);
			for (int i = elements.getLeftShift(); i < elements.getRightShift() + 1; i++) {
				int start = i;
				int end = i + 1;

				elements.get(elements.size() - 1).setStartTime(
						MBData.startTime + start * MBData.period);
				elements.get(elements.size() - 1).setEndTime(
						MBData.startTime + end * MBData.period);
			}
			elements.update(radius, boundSize);
		}
	}

	private void setCircosGlobal() {
		float max = 0;

		for (int i = 0; i < elementLists.size(); i++)
			if (elementLists.get(i) != null
					&& elementLists.get(i).getTemporalElements() != null)
				for (TemporalElement te : elementLists.get(i)
						.getTemporalElements())
					if (te.getCircos().getData().getTotalSum() > max)
						max = te.getCircos().getData().getTotalSum();

		for (int i = 0; i < elementLists.size(); i++)
			if (elementLists.get(i) != null
					&& elementLists.get(i).getTemporalElements() != null)
				for (TemporalElement te : elementLists.get(i)
						.getTemporalElements())
					te.getCircos().setGlobalMax(max);
	}

	public void reset() {
		selected = null;
		sr.selectInterchange(-1, -1);
		MBData.TIME_CHANGED = true;
		elementLists.removeAll(elementLists);
	}
}
